/**
 * jQuery jPages v0.7
 * Client side pagination with jQuery
 * http://luis-almeida.github.com/jPages
 *
 * Licensed under the MIT license.
 * Copyright 2012 Luís Almeida
 * https://github.com/luis-almeida
 */

;
(function ($, window, document, undefined) {

    var name = "jPages",
        instance = null,
        defaults = {
            containerID: "",
            first: false,
            previous: "← previous",
            next: "next →",
            last: false,
            links: "numeric", // blank || title
            startPage: 1,
            perPage: 10,
            midRange: 5,
            startRange: 1,
            endRange: 1,
            keyBrowse: false,
            scrollBrowse: false,
            pause: 0,
            clickStop: false,
            delay: 50,
            direction: "forward", // backwards || auto || random ||
            animation: "", // http://daneden.me/animate/ - any entrance animations
            fallback: 400,
            minHeight: true,
            allowSearch: false,
            tdNumForSearch: 0,
            searchContainer: '',
            label: '',
            callback: undefined // function( pages, items ) { }
        };


    function Plugin(element, options) {
        this.options = $.extend({}, defaults, options);
        this.allowSearch = this.options.allowSearch;
        this.tdNumForSearch = this.options.tdNumForSearch;
        this.searchContainer = this.options.searchContainer;
        this.label = this.options.label;
        if (this.allowSearch) {
            this.writeSearch();
        }
        this._container = $("#" + this.options.containerID);
        if (!this._container.length) return;

        this.jQwindow = $(window);
        this.jQdocument = $(document);

        this._holder = $(element);
        this._nav = {};

        this._first = $(this.options.first);
        this._previous = $(this.options.previous);
        this._next = $(this.options.next);
        this._last = $(this.options.last);

        /* only visible items! */
        this._items = this._container.children(":visible");
        this._itemsShowing = $([]);
        this._itemsHiding = $([]);

        this._numPages = Math.ceil(this._items.length / this.options.perPage);
        this._currentPageNum = this.options.startPage;

        this._clicked = false;
        this._cssAnimSupport = this.getCSSAnimationSupport();

        this.init();
    }

    Plugin.prototype = {

        constructor: Plugin,

        getCSSAnimationSupport: function () {
            var animation = false,
                animationstring = 'animation',
                keyframeprefix = '',
                domPrefixes = 'Webkit Moz O ms Khtml'.split(' '),
                pfx = '',
                elm = this._container.get(0);

            if (elm.style.animationName) animation = true;

            if (animation === false) {
                for (var i = 0; i < domPrefixes.length; i++) {
                    if (elm.style[domPrefixes[i] + 'AnimationName'] !== undefined) {
                        pfx = domPrefixes[i];
                        animationstring = pfx + 'Animation';
                        keyframeprefix = '-' + pfx.toLowerCase() + '-';
                        animation = true;
                        break;
                    }
                }
            }

            return animation;
        },

        init: function () {
            this.setStyles();
            this.setNav();
            this.paginate(this._currentPageNum);
            this.setMinHeight();
            if (this.options.sortParams && this.options.sortParams.length > 0) {
                this.bindSortFun();
            }
        },

        setStyles: function () {
            var requiredStyles = "<style>" +
                ".jp-invisible { visibility: hidden !important; } " +
                ".jp-hidden { display: none !important; }" +
                "</style>";

            $(requiredStyles).appendTo("head");

            if (this._cssAnimSupport && this.options.animation.length)
                this._items.addClass("animated jp-hidden");
            else this._items.hide();

        },

        setNav: function () {
            var navhtml = this.writeNav();

            this._holder.each(this.bind(function (index, element) {
                var holder = $(element);
                holder.html(navhtml);
                this.cacheNavElements(holder, index);
                this.bindNavHandlers(index);
                this.disableNavSelection(element);
            }, this));

            if (this.options.keyBrowse) this.bindNavKeyBrowse();
            if (this.options.scrollBrowse) this.bindNavScrollBrowse();
        },

        writeNav: function () {
            var i = 1, navhtml;
            navhtml = this.writeBtn("first") + this.writeBtn("previous");

            for (; i <= this._numPages; i++) {
                if (i === 1 && this.options.startRange === 0) navhtml += "<span>...</span>";
                if (i > this.options.startRange && i <= this._numPages - this.options.endRange)
                    navhtml += "<a href='#' class='jp-hidden'>";
                else
                    navhtml += "<a>";

                switch (this.options.links) {
                    case "numeric":
                        navhtml += i;
                        break;
                    case "blank":
                        break;
                    case "title":
                        var title = this._items.eq(i - 1).attr("data-title");
                        navhtml += title !== undefined ? title : "";
                        break;
                }

                navhtml += "</a>";
                if (i === this.options.startRange || i === this._numPages - this.options.endRange)
                    navhtml += "<span>...</span>";
            }
            navhtml += this.writeBtn("next") + this.writeBtn("last") + "</div>";
            return navhtml;
        },

        writeBtn: function (which) {

            return this.options[which] !== false && !$(this["_" + which]).length ?
            "<a class='jp-" + which + "'>" + this.options[which] + "</a>" : "";

        },

        writeSearch: function () {
            var searchDiv = $("#search").length ? $("#search") : $("#" + this.searchContainer);
            if (searchDiv.length) {
                searchDiv.append("<div class='wrapper-green' title='" + this.options.label + "'><div class='searchMeme-button-right searchMeme-round-right green-normal'><div class='searchMeme-button-icon searchMeme-button-inner'></div></div><div class='searchMeme-input-right'><input type='text' id='search-green' class='text_search search-input searchMeme-water-mark'/><input type='button' value='搜索' class='search_btn'/></div></div></div>");
                var search_input = $("#" + this.searchContainer + ">.wrapper-green>.searchMeme-input-right>.search-input");
                var search_btn = $("#" + this.searchContainer + ">.wrapper-green>.searchMeme-button-right");
                search_input.bind("focus.jPages", this.bind(function (event) {
                    var value = $("#" + this.searchContainer + ">.wrapper-green>.searchMeme-input-right>.search-input").val();
                    if (value != '') {
                        $("#" + this.searchContainer + ">.wrapper-green>.searchMeme-input-right>.search-input").val('');
                        this.filterColumn(this.tdNumForSearch);
                    }
                }, this));
                search_input.bind("keydown.jPages", this.bind(function (event) {
                    if (event.keyCode == 13) {
                        this.filterColumn(this.tdNumForSearch);
                        $("#" + this.searchContainer + ">.wrapper-green>.searchMeme-input-right>.search-input").blur();
                    }
                }, this));
                search_input.bind("input.jPages propertychange.jPages",this.bind(function(event){
                    this.filterColumn(this.tdNumForSearch);
                },this));
                $("#" + this.searchContainer + " .search_btn").bind("click.jPages", this.bind(function (event) {
                    this.filterColumn(this.tdNumForSearch);
                }, this));
                search_btn.bind("click.jPages", this.bind(function (event) {
                    this.filterColumn(this.tdNumForSearch);
                }, this));

            }
        },
        filterColumn: function (tdNumForSearch) {
            if (this.options.beforeSearchFun && typeof this.options.beforeSearchFun == 'function') {
                this.options.beforeSearchFun.call();
            }
            if (!this._container.length) return;
            var value = $("#" + this.searchContainer + ">.wrapper-green>.searchMeme-input-right>.search-input").val();
            this._container.children(".nodataTip").empty();
            this._container.children(".nodataTip").remove();
            $('#' + this.options.containerID + ' tr').each(
                function () {
                    $(this).removeClass();
                    $(this).css('display', '');
                    $(this).children().each(
                        function (iCellCount) {
                            if (iCellCount === tdNumForSearch) {
                                var sVal = $(this).text().replace(/(\n)|(\r)/ig, '').replace(/\s\s/ig, ' ').replace(/^\s/ig, '');
                                $(this).removeClass('_match');
                                if (value == '' || sVal.toLowerCase().indexOf(value.toLowerCase()) >= 0) {
                                    $(this).addClass('_match');
                                }
                            }
                        }
                    );
                    if ($(this).children('._match').length == 0) {
                        $(this).css('display', 'none');
                    }
                }
            );
            this._items = this._container.children(":visible");
            if (this._items.length === 0) {
                if (this._container.children(".nodataTip").length) {
                } else {
                    this._container.append(this.options.nodataTip);
                }
                this._container.children(".nodataTip").show()
            }
            this._itemsShowing = $([]);
            this._itemsHiding = $([]);

            this._numPages = Math.ceil(this._items.length / this.options.perPage);
            this._currentPageNum = 1;
            this.setStyles();
            this.setNav();
            this.paginate(this._currentPageNum);
            this.setMinHeight();
        },
        bindSortFun: function () {
            for (var i in this.options.sortParams) {
                var spanId = this.options.sortParams[i].spanId;
                var iCol = this.options.sortParams[i].iCol;
                var dataType = this.options.sortParams[i].dataType;
                var sortFun = this.options.sortParams[i].sortFun;
                var tableId = this.options.sortParams[i].tableId;
                var sortSpan = $("#" + spanId);
                if (sortSpan.length) {
                    sortSpan.parent().bind("click.jPages", {
                        tableId: tableId,
                        iCol: iCol,
                        dataType: dataType,
                        spanId: spanId
                    }, this.bind(function (event) {
                        if (sortFun && typeof sortFun == 'function') {
                            sortFun(event.data.tableId, event.data.iCol, event.data.dataType, event.data.spanId);
                        }
                        if (this.allowSearch) {
                            this.filterColumn(this.tdNumForSearch);
                        } else {
                            $('#' + this.options.containerID + ' tr').each(function () {
                                $(this).removeClass();
                                $(this).css('display', '');
                            });
                            this._currentPageNum = 1;
                            this._items = this._container.children(":visible");
                            this._itemsShowing = $([]);
                            this._itemsHiding = $([]);

                            this._numPages = Math.ceil(this._items.length / this.options.perPage);
                            this._currentPageNum = 1;
                            this.setStyles();
                            this.setNav();
                            this.paginate(this._currentPageNum);
                            this.setMinHeight();
                        }
                    }, this));
                }
            }
        },
        cacheNavElements: function (holder, index) {
            this._nav[index] = {};
            this._nav[index].holder = holder;
            this._nav[index].first = this._first.length ? this._first : this._nav[index].holder.find("a.jp-first");
            this._nav[index].previous = this._previous.length ? this._previous : this._nav[index].holder.find("a.jp-previous");
            this._nav[index].next = this._next.length ? this._next : this._nav[index].holder.find("a.jp-next");
            this._nav[index].last = this._last.length ? this._last : this._nav[index].holder.find("a.jp-last");
            this._nav[index].fstBreak = this._nav[index].holder.find("span:first");
            this._nav[index].lstBreak = this._nav[index].holder.find("span:last");
            this._nav[index].pages = this._nav[index].holder.find("a").not(".jp-first, .jp-previous, .jp-next, .jp-last");
            this._nav[index].permPages =
                this._nav[index].pages.slice(0, this.options.startRange)
                    .add(this._nav[index].pages.slice(this._numPages - this.options.endRange, this._numPages));
            this._nav[index].pagesShowing = $([]);
            this._nav[index].currentPage = $([]);
        },

        bindNavHandlers: function (index) {
            var nav = this._nav[index];

            // default nav
            nav.holder.bind("click.jPages", this.bind(function (evt) {
                var newPage = this.getNewPage(nav, $(evt.target));
                if (this.validNewPage(newPage)) {
                    this._clicked = true;
                    this.paginate(newPage);
                }
                evt.preventDefault();
            }, this));

            // custom first
            if (this._first.length) {
                this._first.bind("click.jPages", this.bind(function () {
                    if (this.validNewPage(1)) {
                        this._clicked = true;
                        this.paginate(1);
                    }
                }, this));
            }

            // custom previous
            if (this._previous.length) {
                this._previous.bind("click.jPages", this.bind(function () {
                    var newPage = this._currentPageNum - 1;
                    if (this.validNewPage(newPage)) {
                        this._clicked = true;
                        this.paginate(newPage);
                    }
                }, this));
            }

            // custom next
            if (this._next.length) {
                this._next.bind("click.jPages", this.bind(function () {
                    var newPage = this._currentPageNum + 1;
                    if (this.validNewPage(newPage)) {
                        this._clicked = true;
                        this.paginate(newPage);
                    }
                }, this));
            }

            // custom last
            if (this._last.length) {
                this._last.bind("click.jPages", this.bind(function () {
                    if (this.validNewPage(this._numPages)) {
                        this._clicked = true;
                        this.paginate(this._numPages);
                    }
                }, this));
            }

        },

        disableNavSelection: function (element) {
            if (typeof element.onselectstart != "undefined")
                element.onselectstart = function () {
                    return false;
                };
            else if (typeof element.style.MozUserSelect != "undefined")
                element.style.MozUserSelect = "none";
            else
                element.onmousedown = function () {
                    return false;
                };
        },

        bindNavKeyBrowse: function () {
            this.jQdocument.bind("keydown.jPages", this.bind(function (evt) {
                var target = evt.target.nodeName.toLowerCase();
                if (this.elemScrolledIntoView() && target !== "input" && target != "textarea") {
                    var newPage = this._currentPageNum;

                    if (evt.which == 37) newPage = this._currentPageNum - 1;
                    if (evt.which == 39) newPage = this._currentPageNum + 1;

                    if (this.validNewPage(newPage)) {
                        this._clicked = true;
                        this.paginate(newPage);
                    }
                }
            }, this));
        },

        elemScrolledIntoView: function () {
            var docViewTop, docViewBottom, elemTop, elemBottom;
            docViewTop = this.jQwindow.scrollTop();
            docViewBottom = docViewTop + this.jQwindow.height();
            elemTop = this._container.offset().top;
            elemBottom = elemTop + this._container.height();
            return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom));

            // comment above and uncomment below if you want keyBrowse to happen
            // only when container is completely visible in the page
            /*return ((elemBottom >= docViewTop) && (elemTop <= docViewBottom) &&
             (elemBottom <= docViewBottom) &&  (elemTop >= docViewTop) );*/
        },

        bindNavScrollBrowse: function () {
            this._container.bind("mousewheel.jPages DOMMouseScroll.jPages", this.bind(function (evt) {
                var newPage = (evt.originalEvent.wheelDelta || -evt.originalEvent.detail) > 0 ?
                    (this._currentPageNum - 1) : (this._currentPageNum + 1);
                if (this.validNewPage(newPage)) {
                    this._clicked = true;
                    this.paginate(newPage);
                }
                evt.preventDefault();
                return false;
            }, this));
        },

        getNewPage: function (nav, target) {
            if (target.is(nav.currentPage)) return this._currentPageNum;
            if (target.is(nav.pages)) return nav.pages.index(target) + 1;
            if (target.is(nav.first)) return 1;
            if (target.is(nav.last)) return this._numPages;
            if (target.is(nav.previous)) return nav.pages.index(nav.currentPage);
            if (target.is(nav.next)) return nav.pages.index(nav.currentPage) + 2;
        },

        validNewPage: function (newPage) {
            return newPage !== this._currentPageNum && newPage > 0 && newPage <= this._numPages;
        },

        paginate: function (page) {
            var itemRange, pageInterval;
            itemRange = this.updateItems(page);
            pageInterval = this.updatePages(page);
            this._currentPageNum = page;
            if ($.isFunction(this.options.callback))
                this.callback(page, itemRange, pageInterval);

            this.updatePause();
        },

        updateItems: function (page) {
            var range = this.getItemRange(page);
            this._itemsHiding = this._itemsShowing;
            this._itemsShowing = this._items.slice(range.start, range.end);
            if (this._cssAnimSupport && this.options.animation.length) this.cssAnimations(page);
            else this.jQAnimations(page);
            return range;
        },

        getItemRange: function (page) {
            var range = {};
            range.start = (page - 1) * this.options.perPage;
            range.end = range.start + this.options.perPage;
            if (range.end > this._items.length) range.end = this._items.length;
            return range;
        },

        cssAnimations: function (page) {
            clearInterval(this._delay);

            this._itemsHiding
                .removeClass(this.options.animation + " jp-invisible")
                .addClass("jp-hidden");

            this._itemsShowing
                .removeClass("jp-hidden")
                .addClass("jp-invisible");

            this._itemsOriented = this.getDirectedItems(page);
            this._index = 0;

            this._delay = setInterval(this.bind(function () {
                if (this._index === this._itemsOriented.length) clearInterval(this._delay);
                else {
                    this._itemsOriented
                        .eq(this._index)
                        .removeClass("jp-invisible")
                        .addClass(this.options.animation);
                }
                this._index = this._index + 1;
            }, this), this.options.delay);
        },

        jQAnimations: function (page) {
            clearInterval(this._delay);
            this._itemsHiding.addClass("jp-hidden");
            this._itemsShowing.fadeTo(0, 0).removeClass("jp-hidden");
            this._itemsOriented = this.getDirectedItems(page);
            this._index = 0;
            this._delay = setInterval(this.bind(function () {
                if (this._index === this._itemsOriented.length) clearInterval(this._delay);
                else {
                    this._itemsOriented
                        .eq(this._index)
                        .fadeTo(this.options.fallback, 1);
                }
                this._index = this._index + 1;
            }, this), this.options.delay);
        },

        getDirectedItems: function (page) {
            var itemsToShow;

            switch (this.options.direction) {
                case "backwards":
                    itemsToShow = $(this._itemsShowing.get().reverse());
                    break;
                case "random":
                    itemsToShow = $(this._itemsShowing.get().sort(function () {
                        return (Math.round(Math.random()) - 0.5);
                    }));
                    break;
                case "auto":
                    itemsToShow = page >= this._currentPageNum ?
                        this._itemsShowing : $(this._itemsShowing.get().reverse());
                    break;
                default:
                    itemsToShow = this._itemsShowing;
            }

            return itemsToShow;
        },

        updatePages: function (page) {
            var interval, index, nav;
            interval = this.getInterval(page);
            for (index in this._nav) {
                if (this._nav.hasOwnProperty(index)) {
                    nav = this._nav[index];
                    this.updateBtns(nav, page);
                    this.updateCurrentPage(nav, page);
                    this.updatePagesShowing(nav, interval);
                    this.updateBreaks(nav, interval);
                }
            }
            return interval;
        },

        getInterval: function (page) {
            var neHalf, upperLimit, start, end;
            neHalf = Math.ceil(this.options.midRange / 2);
            upperLimit = this._numPages - this.options.midRange;
            start = page > neHalf ? Math.max(Math.min(page - neHalf, upperLimit), 0) : 0;
            end = page > neHalf ?
                Math.min(page + neHalf - (this.options.midRange % 2 > 0 ? 1 : 0), this._numPages) :
                Math.min(this.options.midRange, this._numPages);
            return {start: start, end: end};
        },

        updateBtns: function (nav, page) {
            if (page === 1) {
                nav.first.addClass("jp-disabled");
                nav.previous.addClass("jp-disabled");
            }
            if (page === this._numPages) {
                nav.next.addClass("jp-disabled");
                nav.last.addClass("jp-disabled");
            }
            if (this._currentPageNum === 1 && page > 1) {
                nav.first.removeClass("jp-disabled");
                nav.previous.removeClass("jp-disabled");
            }
            if (this._currentPageNum === this._numPages && page < this._numPages) {
                nav.next.removeClass("jp-disabled");
                nav.last.removeClass("jp-disabled");
            }
        },

        updateCurrentPage: function (nav, page) {
            nav.currentPage.removeClass("jp-current");
            nav.currentPage = nav.pages.eq(page - 1).addClass("jp-current");
        },

        updatePagesShowing: function (nav, interval) {
            var newRange = nav.pages.slice(interval.start, interval.end).not(nav.permPages);
            nav.pagesShowing.not(newRange).addClass("jp-hidden");
            newRange.not(nav.pagesShowing).removeClass("jp-hidden");
            nav.pagesShowing = newRange;
        },

        updateBreaks: function (nav, interval) {
            if (
                interval.start > this.options.startRange ||
                (this.options.startRange === 0 && interval.start > 0)
            ) nav.fstBreak.removeClass("jp-hidden");
            else nav.fstBreak.addClass("jp-hidden");

            if (interval.end < this._numPages - this.options.endRange) nav.lstBreak.removeClass("jp-hidden");
            else nav.lstBreak.addClass("jp-hidden");
        },

        callback: function (page, itemRange, pageInterval) {
            var pages = {
                    current: page,
                    interval: pageInterval,
                    count: this._numPages
                },
                items = {
                    showing: this._itemsShowing,
                    oncoming: this._items.slice(itemRange.start + this.options.perPage, itemRange.end + this.options.perPage),
                    range: itemRange,
                    count: this._items.length
                };

            pages.interval.start = pages.interval.start + 1;
            items.range.start = items.range.start + 1;
            this.options.callback(pages, items);
        },

        updatePause: function () {
            if (this.options.pause && this._numPages > 1) {
                clearTimeout(this._pause);
                if (this.options.clickStop && this._clicked) return;
                else {
                    this._pause = setTimeout(this.bind(function () {
                        this.paginate(this._currentPageNum !== this._numPages ? this._currentPageNum + 1 : 1);
                    }, this), this.options.pause);
                }
            }
        },

        setMinHeight: function () {
            if (this.options.minHeight && !this._container.is("table, tbody")) {
                setTimeout(this.bind(function () {
                    this._container.css({"min-height": this._container.css("height")});
                }, this), 1000);
            }
        },

        bind: function (fn, me) {
            return function () {
                return fn.apply(me, arguments);
            };
        },

        destroy: function () {
            this.jQdocument.unbind("keydown.jPages");
            this._container.unbind("mousewheel.jPages DOMMouseScroll.jPages");

            if (this.options.minHeight) this._container.css("min-height", "");
            if (this._cssAnimSupport && this.options.animation.length)
                this._items.removeClass("animated jp-hidden jp-invisible " + this.options.animation);
            else this._items.removeClass("jp-hidden").fadeTo(0, 1);
            this._holder.unbind("click.jPages").empty();
        }

    };

    $.fn[name] = function (arg) {
        var type = $.type(arg);
        if (type === "object") {
            if (this.length && !$.data(this, name)) {
                instance = new Plugin(this, arg);
                this.each(function () {
                    $.data(this, name, instance);
                });
            }
            return this;
        }

        if (type === "string" && arg === "destroy") {
            instance.destroy();
            this.each(function () {
                $.removeData(this, name);
            });
            return this;
        }

        if (type === 'number' && arg % 1 === 0) {
            if (instance.validNewPage(arg)) instance.paginate(arg);
            return this;
        }

        return this;
    };

})(jQuery, window, document);
